
float4x4 orthoProjMatrix;
float3x4 transformMatrix;
float4 elemSize;
float4 strokeWidth;
float4 rectParams;
float4 viewportSize;

texture brushTexture;

sampler brushSampler = sampler_state
{
	Texture = <brushTexture>;
	AddressU = CLAMP;
	AddressV = CLAMP;
	AddressW = CLAMP;
	BorderColor = float4(0, 0, 0, 0);
	MipFilter = LINEAR;
	MinFilter = LINEAR;
	MagFilter = LINEAR;
};

struct VertexShaderInput
{
	float3 position : POSITION0;
	float4 color : COLOR0;
	float2 texCoord : TEXCOORD0;
};

struct PixelShaderInput
{
	float4 position : POSITION0;
	float2 texCoord : TEXCOORD0;
	float4 color : TEXCOORD1;
};

void VertexMain(in VertexShaderInput vsInput, out PixelShaderInput output)
{
	output = (PixelShaderInput)0;

	float3 newPosTransform = mul(float3(vsInput.position.xy, 1), float3x3(transformMatrix._11_12_13, transformMatrix._21_22_23, transformMatrix._31_32_33));
	newPosTransform.xy /= newPosTransform.z;

	float4 posTransformed = float4(newPosTransform.xy, 0, 1);
	float xbase = -viewportSize.x / 2.0f;
	float ybase = viewportSize.y / 2.0f;
	posTransformed.x += xbase;
	posTransformed.y = ybase - posTransformed.y;

	output.position = mul(posTransformed, orthoProjMatrix);
	output.position.x -= 1.0f / (2 * viewportSize.x);
	output.position.y -= 1.0f / (2 * viewportSize.y);
	output.texCoord = vsInput.texCoord;
	output.color = vsInput.color;
}

float getEllipseRadius(float x, float y)
{
	float phi = atan2(y, x);
	float a2 = rectParams.x * rectParams.x;
	float b2 = rectParams.y * rectParams.y;
	float ab = rectParams.x * rectParams.y;
	float sin2 = sin(phi) * sin(phi);
	float cos2 = cos(phi) * cos(phi);

	return (ab) / sqrt(a2 * sin2 + b2 * cos2);
}

float4 sinusInterpolate(float4 src, float4 dest, float pct)
{
	float sinval = sin(pct * 3.1415926 / 2.0f);
	return sinval * src + (1 - sinval) * dest;
}

// Returns:
// 0 -> top left
// 1 -> bottom left
// 2 -> top right
// 3 -> bottom right
// -2 -> "anti alias area"
// -1 -> inside the solid area
int getBorderQuad(float2 pos)
{
	if(pos.x <= rectParams.x + 2 && pos.y <= rectParams.y + 2)
		return 0;
	else if(pos.x <= rectParams.x + 2 && (elemSize.y - pos.y) <= rectParams.y + 2)
		return 1;
	else if((elemSize.x - pos.x) <= rectParams.x + 2 && pos.y <= rectParams.y + 2)
		return 2;
	else if((elemSize.x - pos.x) <= rectParams.x + 2 && (elemSize.y - pos.y) <= rectParams.y + 2)
		return 3;

	if(pos.x <= 2 || pos.y <= 2 || (elemSize.x - pos.x) <= 2 || (elemSize.y - pos.y) <= 2)
		return -2;

	return -1;
}

float4 getColorFill(float4 color, float2 pos, bool fill, float2 texCoord)
{
	int quad = getBorderQuad(pos);
	
	if(quad == -2)
	{
		float pct = 0.0f;
		if(pos.x <= 2)
			pct = (2 - pos.x) / 2.0f;
		else if(pos.x >= (elemSize.x - 2))
			pct = (pos.x - elemSize.x + 2) / 2.0f;
		else if(pos.y <= 2)
			pct = (2 - pos.y) / 2.0f;
		else if(pos.y >= (elemSize.y - 2))
			pct = (pos.y - elemSize.y + 2) / 2.0f;

		return sinusInterpolate(color, float4(color.rgb, 0), 1 - pct);
	}

	if(quad == -1)
	{
		if(fill)
			return color;

		float strokex = strokeWidth.x;
		float strokey = strokeWidth.y;

		float cy = 2 / elemSize.y;

		if((pos.x - 2) <= strokex)
			return color;
		if((texCoord.y - cy) <= strokey)
			return color;
		if((elemSize.x - 2 - pos.x) <= strokex)
			return color;
		if((1 - cy - texCoord.y) <= strokey)
			return color;

		float pct = 0.0f;
		if(pos.x <= 4)
			pct = (2 - pos.x + 2) / 2.0f;
		else if(pos.y <= 4)
			pct = (2 - pos.y + 2) / 2.0f;
		else if((elemSize.x - pos.x) <= 4)
			pct = (elemSize.x - 2 - pos.x) / 2.0f;
		else if((elemSize.y - pos.y) <= 4)
			pct = (elemSize.y - 2 - pos.y) / 2.0f;
		else
			return float4(color.rgb, 0);

		return sinusInterpolate(color, float4(color.rgb, 0), 1 - pct);
	}

	float2 midPoint;
	if(quad == 0)
		midPoint = float2(rectParams.x + 2, rectParams.y + 2);
	else if(quad == 1)
		midPoint = float2(rectParams.x + 2, elemSize.y - rectParams.y - 2);
	else if(quad == 2)
		midPoint = float2(elemSize.x - rectParams.x - 2, rectParams.y + 2);
	else if(quad == 3)
		midPoint = float2(elemSize.x - rectParams.x - 2, elemSize.y - rectParams.y - 2);

	float x = pos.x - midPoint.x;
	float y = pos.y - midPoint.y;

	float radius = getEllipseRadius(x, y);
	float radiusCoord = sqrt(x * x + y * y);
	if(fill)
	{
		if(radiusCoord <= radius)
			color = color;
		else
		{
			float diff = radiusCoord - radius;
			if(diff > 2)
				color = float4(color.rgb, 0);
			else
				color = sinusInterpolate(color, float4(color.rgb, 0), 1.0f - diff / 2.0f);
		}

		return color;
	}
	else
	{
		if(radiusCoord <= radius)
		{
			if((radius - radiusCoord) <= strokeWidth.x)
				color = color;
			else
			{
				float diff = (radius - radiusCoord) - strokeWidth.x;
				if(diff > 2)
					color = float4(color.rgb, 0);
				else
					color = sinusInterpolate(color, float4(color.rgb, 0), 1.0f - diff / 2.0f);
			}
		}
		else
		{
			float diff = radiusCoord - radius;
			if(diff > 2)
				color = float4(color.rgb, 0);
			else
				color = sinusInterpolate(color, float4(color.rgb, 0), 1.0f - diff / 2.0f);
		}

		return color;
	}
}

void PixelMainFill(in PixelShaderInput input, out float4 color : COLOR0)
{
	color = getColorFill(tex2D(brushSampler, input.texCoord), float2(elemSize.x * input.texCoord.x, elemSize.y * input.texCoord.y), true, input.texCoord);
}

void PixelMainDraw(in PixelShaderInput input, out float4 color : COLOR0)
{
	color = getColorFill(tex2D(brushSampler, input.texCoord), float2(elemSize.x * input.texCoord.x, elemSize.y * input.texCoord.y), false, input.texCoord);
}